#include <my_header.h>
// 断开重连,多连接客户端
/* Usage: ./3_server_select_reset */
int main(void){                                  
    char *ip = "192.168.229.128";
    char *port= "8080";

    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    ERROR_CHECK(socket_fd, -1, "socket_fd");

    // 断开重连
    int reuse = 1;
    setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    // 构建 struct sockaddr 结构体类型
    struct sockaddr_in sockaddr;
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(atoi(port));
    sockaddr.sin_addr.s_addr = inet_addr(ip);

    // 绑定ip地址和端口
    int ret_bind = bind(socket_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
    ERROR_CHECK(ret_bind, -1, "bind");

    // 监听socket端的ip地址和端口
    int res_listen = listen(socket_fd, 10);
    // 从这里开始socket_fd变为监听套接字 监听队列
    ERROR_CHECK(res_listen, -1, "listen");

    // 定义监听集合
    // 新的套接字存储新客户端的对象
    int net_fd;
    // set的副本 select函数会清空set集合
    fd_set base_set;
    FD_ZERO(&base_set);
    FD_SET(socket_fd, &base_set);
    while(1)
    {
        // 拷贝base_set的集合
        fd_set set;
        memcpy(&set, &base_set, sizeof(base_set));

        // select监听集合
        int res_select = select(100, &set, NULL, NULL, NULL);
        ERROR_CHECK(res_select, -1, "select");

        if(FD_ISSET(socket_fd, &set))
        {
            // 获取新的一个socket对象 获取连接
            net_fd = accept(socket_fd, NULL, NULL);
            FD_SET(net_fd, &base_set);
            FD_SET(STDIN_FILENO, &base_set);
            FD_CLR(socket_fd, &base_set);
            printf("--对方已上线-- \n");
            continue;
        }
        if(FD_ISSET(STDIN_FILENO, &set))
        {
            // 读取标准输入
            char buf[60] = { 0 };
            int res_read = read(STDIN_FILENO, buf, sizeof(buf));
            if(res_read == 0)
            {
                printf("服务端：断开连接 \n");
                break;
            }
            int res_send = send(net_fd, buf, sizeof(buf), 0);
            ERROR_CHECK(res_send, -1, "send");
        }
        if(FD_ISSET(net_fd, &set))
        {
            // 读取网络数据-》打印
            char buf[60] = { 0 };
            ssize_t ret_recv = recv(net_fd, buf, sizeof(buf), 0);
            if(ret_recv == 0)
            {
                printf("--连接断开-- \n");
                FD_CLR(STDIN_FILENO, &base_set);
                FD_CLR(net_fd, &base_set);
                close(net_fd);
                FD_SET(socket_fd, &base_set);
                continue;
            }
            printf("client message :%s\n", buf);
        }
    }

    close(net_fd);
    close(socket_fd);
    return 0;
}

#if 0
为什么需要一个新的 fd_set：
保留基础集：
使用 base_set 是为了维护一个稳定的集合，以便在后续的
select() 调用中可以继续使用。而 set 是一个临时的集合，
在每次调用 select() 之前都会被清空并重新填充。

避免修改原始集：
select() 可以修改传入的 fd_set，如果我们直接使用
base_set，会影响到下一次的 select() 调用，从而导致
程序行为不正确。

通过这种方式，您能确保每一次 select() 调用都是基于
正确且最新的文件描述符集合进行的，同时也维护了一个
原始的基础集合以用作后续的参考。
#endif
